home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / ICMPCMD.C < prev    next >
Text File  |  1993-08-09  |  13KB  |  451 lines

  1. /* ICMP-related user commands */
  2. #include <stdio.h>
  3.  
  4. #include "global.h"
  5. #include "icmp.h"
  6. #include "ip.h"
  7. #include "mbuf.h"
  8. #include "netuser.h"
  9. #include "internet.h"
  10. #include "timer.h"
  11. #include "socket.h"
  12. #include "proc.h"
  13. #include "session.h"
  14. #include "cmdparse.h"
  15. #include "commands.h"
  16. #include "tcp.h"         /* used for rtt_add() */
  17. #include "domain.h"        /* used for autoping with domain-cache */
  18.  
  19. static void pingtx __ARGS((int s,void *ping1,void *p));
  20. static void _setping(void *x);
  21.  
  22. int Icmp_trace = FALSE;
  23. static int Icmp_echo = TRUE;
  24.  
  25. /* -------------------------- ICMP subcmds -------------------------------- */
  26. static int
  27. doicmpec(int argc,char **argv,void *p)
  28. {
  29.    return setbool(&Icmp_echo,"ICMP echo response accept",argc,argv);
  30. }
  31.  
  32. static int
  33. doicmpstat(int argc,char **argv,void *p)
  34. {
  35.     int i, j;
  36.  
  37.     /* Note that the ICMP variables are shown in column order, because
  38.      * that lines up the In and Out variables on the same line
  39.      */
  40.     for(j = i = 1; i <= NUMICMPMIB; i++) {
  41.         tprintf("(%2u)icmp%-16s%10lu%s",
  42.             i,
  43.             Icmp_mib[i].name,
  44.             Icmp_mib[i].value.integer,
  45.             (j++ % 2) ? "     " : "\n");
  46.     }
  47.     if((j % 2) == 0) {
  48.         tputs("\n");
  49.     }
  50.     return 0;
  51. }
  52.  
  53. static int
  54. doicmptr(int argc,char **argv,void *p)
  55. {
  56.    return setbool(&Icmp_trace,"ICMP trace",argc,argv);
  57. }
  58.  
  59. int
  60. doicmp(int argc,char **argv,void *p)
  61. {
  62.     struct cmds Icmpcmds[] = {
  63.         "echo",         doicmpec,       0, 0, NULLCHAR,
  64.         "status",       doicmpstat,     0, 0, NULLCHAR,
  65.         "trace",        doicmptr,       0, 0, NULLCHAR,
  66.         NULLCHAR
  67.     };
  68.  
  69.    return subcmd(Icmpcmds,argc,argv,p);
  70. }
  71.  
  72. /* ------------------------------------------------------------------------ */
  73.  
  74. /* Send ICMP Echo Request packets */
  75. int
  76. doping(int argc,char **argv,void *p)
  77. {
  78.     struct proc *pinger = NULLPROC;         /* Transmit process */
  79.     struct sockaddr_in from;
  80.     struct icmp icmp;
  81.     struct mbuf *bp;
  82.     int32 timestamp, rtt;
  83.     int s, fromlen;
  84.     struct ping ping;
  85.     struct session *sp;
  86.  
  87.     memset(&ping,0,sizeof(struct ping));
  88.  
  89.     if((ping.target = resolve(argv[1])) == 0) {
  90.         tprintf(Badhost,argv[1]);
  91.         return -1;
  92.     }
  93.     if((s = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1) {
  94.         tputs(Nosocket);
  95.         return -1;
  96.     }
  97.     if(argc > 2) {
  98.         ping.len = (int16)min(atoi(argv[2]),1000);
  99.     }
  100.     if(argc < 4) {
  101.         /* One shot ping; let echo_proc hook handle response.
  102.          * An ID of MAXINT16 will not be confused with a legal socket
  103.          * number, which is used to identify repeated pings
  104.          */
  105.         pingem(s,ping.target,0,MAXINT16,ping.len);
  106.         return 0;
  107.     }
  108.     ping.interval = atol(argv[3]) * 1000L;
  109.  
  110.     /* Optionally ping a range of IP addresses */
  111.     if(argc > 4) {
  112.         ping.incflag = 1;
  113.     }
  114.     /* Allocate a session descriptor */
  115.     if((sp = ping.sp = newsession(argv[1],PING,0)) == NULLSESSION){
  116.         tputs(Nosess);
  117.         close_s(s);
  118.         return 1;
  119.     }
  120.     sp->s = s;
  121.  
  122.     pinger = newproc("pingtx",768,pingtx,s,&ping,NULL,0);
  123.  
  124.     /* Now collect the replies */
  125.     for(;;){
  126.         fromlen = sizeof(from);
  127.  
  128.         if(recv_mbuf(s,&bp,0,(char *)&from,&fromlen) == -1) {
  129.             break;
  130.         }
  131.         ntohicmp(&icmp,&bp);
  132.  
  133.         if(icmp.type != ICMP_ECHO_REPLY || icmp.args.echo.id != s) {
  134.             /* Ignore other people's responses */
  135.             free_p(bp);
  136.             continue;
  137.         }
  138. /*        ping.responses++;    */
  139.  
  140.         /* Get stamp */
  141.         if(pullup(&bp,(char *)×tamp,sizeof(timestamp)) != sizeof(timestamp)){
  142.             /* The timestamp is missing! */
  143.             free_p(bp);     /* Probably not necessary */
  144.             continue;
  145.         }
  146.         free_p(bp);
  147.  
  148.         /* Compute round trip time, update smoothed estimates */
  149.         rtt = msclock() - timestamp;
  150.         rtt_add(from.sin_addr.s_addr,(rtt * 3) / 2);
  151.  
  152.         if(ping.incflag) {
  153.             tprintf("%s: rtt %lu\n",inet_ntoa(from.sin_addr.s_addr), rtt);
  154.             continue;
  155.         }
  156.         if(++ping.responses == 1){
  157.             /* First response, base entire SRTT on it */
  158.             ping.srtt = rtt;
  159.             ping.mdev = 0;
  160.         } else {
  161.             int32 abserr = (rtt > ping.srtt) ? (rtt-ping.srtt) : (ping.srtt-rtt);
  162.             ping.srtt = (7 * ping.srtt + rtt + 4) >> 3;
  163.             ping.mdev = (3 * ping.mdev + abserr + 2) >> 2;
  164.         }
  165.     }
  166.     if(pinger != NULLPROC) {
  167.         killproc(pinger);
  168.     }
  169.     keywait(NULLCHAR,1);
  170.     freesession(sp);
  171.     return 0;
  172. }
  173.  
  174. void
  175. echo_proc(int32 source,int32 dest,struct icmp *icmp,struct mbuf *bp)
  176. {
  177. int32 timestamp;
  178.  
  179.     if(Icmp_echo && icmp->args.echo.id == MAXINT16
  180.       && pullup(&bp,(char *)×tamp,sizeof(timestamp)) == sizeof(timestamp)) {
  181.         /* Compute round trip time */
  182.         int32 rtt = msclock() - timestamp;
  183.  
  184.         tprintf("%s: rtt %lu\n",inet_ntoa(source),rtt);
  185.         rtt_add(source,(rtt * 3)/2);
  186.     }
  187.     free_p(bp);
  188. }
  189.  
  190. /* Ping transmit process. Runs until killed */
  191. static void
  192. pingtx(int s,void *ping1,void *p)
  193. {
  194. struct ping *ping = (struct ping *)ping1;
  195.  
  196.    if(ping->incflag) {
  197.       for(;;) {
  198.          tprintf("pinging %s...\n",inet_ntoa(ping->target));
  199.          pingem(s,ping->target++,0,(int16)s,ping->len);
  200.          pause(ping->interval);
  201.       }
  202.    } else {
  203.       tprintf("pinging %s...\n",inet_ntoa(ping->target));
  204.       ping->sent = 0;
  205.  
  206.       for(;;) {
  207.         if(ping->sent) {
  208.           tprintf("sent%6lu  rcvd%6lu  %%%4lu  avg rtt%6lu  mdev%5lu\n",
  209.             ping->sent,
  210.             ping->responses,
  211.             (ping->responses * 100 + ping->sent / 2) / ping->sent,
  212.             ping->srtt,
  213.             ping->mdev);
  214.         }
  215.         pingem(s,ping->target,(int16)ping->sent++,(int16)s,ping->len);
  216.         pause(ping->interval);
  217.       }
  218.    }
  219. }
  220.  
  221. /*----------------------------------------------------------------------*
  222. * Send ICMP Echo Request packet                                         *
  223. *-----------------------------------------------------------------------*/
  224. int                            /* DIALER requires a non-static definition */
  225. pingem(
  226. int s,                              /* Raw socket on which to send ping */
  227. int32 target,                       /* Site to be pinged */
  228. int16 seq,                          /* ICMP Echo Request sequence number */
  229. int16 id,                           /* ICMP Echo Request ID */
  230. int16 len)                          /* Length of optional data field */
  231. {
  232.     struct mbuf *bp;
  233.     struct icmp icmp;
  234.     struct sockaddr_in to;
  235.  
  236.     int32 clock = msclock();
  237.     struct mbuf *data = alloc_mbuf(len + sizeof(clock));
  238.  
  239.     data->cnt = len + sizeof(clock);
  240.  
  241.     /* Set optional data field, if any, to all 55's */
  242.     if(len == 64) {
  243.         char *cp = data->data + sizeof(clock), bits = 0x20;
  244.         while(bits < (0x20 + len)) {
  245.             *cp++ = bits++;
  246.         }
  247.     } else if(len != 0) {
  248.         memset(data->data + sizeof(clock),0x55,len);
  249.     }
  250.     /* Insert timestamp and build ICMP header */
  251.     memcpy(data->data,(char *)&clock,sizeof(clock));
  252.     icmpOutEchos++;
  253.     icmpOutMsgs++;
  254.     icmp.type = ICMP_ECHO;
  255.     icmp.code = 0;
  256.     icmp.args.echo.seq = seq;
  257.     icmp.args.echo.id = id;
  258.  
  259.     bp = htonicmp(&icmp,data);
  260.     to.sin_family = AF_INET;
  261.     to.sin_addr.s_addr = target;
  262.     send_mbuf(s,bp,0,(char *)&to,SOCKSIZE);
  263.  
  264.     return 0;
  265. }
  266.  
  267. /*----------------------------------------------------------------------*
  268. * provide a timer triggered ping to signal our presece to RPSF Servers  *
  269. * Syntax:                                                               *
  270. *         setping <address> <interval>                                  *
  271. *-----------------------------------------------------------------------*/
  272.  
  273. /*----------------------------------------------------------------------*
  274. *-----------------------------------------------------------------------*/
  275. int
  276. dosetping(int argc,char **argv, void *p)
  277. {
  278. int i;
  279. int32 target, timeout;
  280. Cache *ap = cache;
  281.  
  282.     if(argc < 2) {
  283.         /*----------------------------------------------------------------*
  284.          * display the Pinglist                                           *
  285.          *----------------------------------------------------------------*/
  286.         return docachelist(argc,argv,p);
  287.     }
  288.     if(argc < 3) {
  289.         /*----------------------------------------------------------------*
  290.          * give usage information                                         *
  291.          *----------------------------------------------------------------*/
  292.         tputs("Usage: setping <address> <interval>\n");
  293.         return -1;
  294.     }
  295.     /*--------------------------------------------------------------------*
  296.      * resolve destination                                                *
  297.      *--------------------------------------------------------------------*/
  298.     if((target = resolve(argv[1])) == 0) {
  299.         tprintf(Badhost,argv[1]);      /* ain't never heard of */
  300.         return -1;
  301.     }
  302.     /*--------------------------------------------------------------------*
  303.      * allocate and fill a list entry                                     *
  304.      *--------------------------------------------------------------------*/
  305.     for(i = 0; i < Dcache_size; i++) {
  306.         if(i == Dcache_size) {
  307.             return -1;
  308.         }
  309.         if(ap->address == target)   {
  310.             break;
  311.         }
  312.         ap++;
  313.     }
  314.     if((timeout = atol(argv[2])) < 60) {
  315.         /* minimum interval is 60 seconds*/
  316.         timeout = 60L;
  317.     }
  318.     ap->timer.func = _setping;                  /* what to call on timeout */
  319.     ap->timer.arg = ap;                            /* dummy value             */
  320.     set_timer(&ap->timer,timeout * 1000L);      /* set timer duration      */
  321. #ifdef MDEBUG
  322.     sprintf(ap->timer.tname,"%.7s",argv[1]);
  323. #endif
  324.     ap->state = Bad;
  325.     /*-------------------------------------------------------------------*
  326.      * just do a one shot ping  and restart the timer                     *
  327.      *--------------------------------------------------------------------*/
  328.     if(ap->proc_run < Activep) {
  329.         _setping(ap);
  330.     }
  331.     return 0;
  332. }
  333.  
  334. /*----------------------------------------------------------------------*
  335. *-----------------------------------------------------------------------*/
  336. int
  337. doresetping(int argc,char **argv, void *p)
  338. {
  339. int i;
  340. int32 target;
  341. Cache *ap = cache;
  342.  
  343.    if((target = resolve(argv[1])) == 0) {
  344.       tprintf(Badhost,argv[1]);      /* ain't never heard of*/
  345.       return 1;
  346.    }
  347.  
  348.    /*-------------------------------------------------------------------*
  349.    * isolate the entry                                                  *
  350.    * ap == entry to be removed                                          *
  351.    *--------------------------------------------------------------------*/
  352.    for(i = 0; i < Dcache_size; i++) {
  353.       if(i == Dcache_size) {
  354.          return 1;
  355.       }
  356.       if(ap->address == target) {
  357.          break;
  358.       }
  359.       ap++;
  360.    }
  361.    if(ap->timer.state == TIMER_STOP)   {
  362.       tprintf("Can't reset %s, autoping is active\n",argv[1]);
  363.       return 1;
  364.    }
  365.    stop_timer(&ap->timer);
  366.    ap->state = Unknown;
  367.  
  368.    return 0;
  369. }
  370.  
  371. static void autoping __ARGS((int a,void *p,void *v));
  372.  
  373. /*----------------------------------------------------------------------*
  374. * this routine is called on each timeout                                *
  375. *-----------------------------------------------------------------------*/
  376. static void
  377. _setping(void *x)
  378. {
  379. Cache *ap = x;
  380. char name[22];
  381.  
  382.    sprintf(name,"AP %s",inet_ntoa(ap->address));
  383.    /*-------------------------------------------------------------------*
  384.    * spawn an Autoping process                                          *
  385.    *--------------------------------------------------------------------*/
  386.    ap->proc_run = Activep;
  387.    newproc(name,512,autoping,ap->state,x,NULL,0);
  388. }
  389.  
  390. /*----------------------------------------------------------------------*
  391. * process to be started for each Autoping                               *
  392. * sorry, but that goto end thing is needed, cuz I have to reset the     *
  393. * 'proc_run" flag upon exit.. and won't leave that up to the optimizer. *
  394. * DK5DC                                                                 *
  395. *-----------------------------------------------------------------------*/
  396. static void
  397. autoping(int oldstate,void *p,void *v)
  398. {
  399.     Cache *ap = p;
  400.     struct sockaddr_in from;
  401.     struct mbuf *bp;
  402.     struct icmp icmp;
  403.     int s, fromlen = SOCKSIZE;
  404.  
  405.     /*-------------------------------------------------------------------*
  406.     * get a socket
  407.     *--------------------------------------------------------------------*/
  408.     if((s = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1){
  409.         tputs(Nosocket);
  410.         goto end1;
  411.     }
  412.     stop_timer(&ap->timer);              /* stop the timer               */
  413.     pingem(s,ap->address,0,s,64);        /* fire up that ping            */
  414.     /*-------------------------------------------------------------------*
  415.     * now wait and collect replies                                       *
  416.     *--------------------------------------------------------------------*/
  417.     alarm(60000L);         /* Let each ping timeout after 60 seconds*/
  418.  
  419.     for(;;) {
  420.         if(recv_mbuf(s,&bp,0,(char *)&from,&fromlen) == -1){
  421.             if(errno == EALARM)            /* We timed out                 */
  422.                 break;
  423.             alarm(0);
  424. /*            ap->state = oldstate;    */
  425.             goto end;
  426.         }
  427.         ntohicmp(&icmp,&bp);
  428.         free_p(bp);
  429.  
  430.         if(icmp.type != ICMP_ECHO_REPLY
  431.           || from.sin_addr.s_addr != ap->address
  432.           || icmp.args.echo.id != s)
  433.             /* Ignore other people's responses */
  434.             continue;
  435.         alarm(0);
  436.         ap->state = Good;                 /* Finally change state         */
  437.         goto end;
  438.     }
  439.     if(ap->state == Good) {
  440.         ap->state = Suspect;
  441.     } else if(ap->state == Suspect) {
  442.         ap->state = Bad;
  443.     }
  444. end:
  445.     close_s(s);
  446. end1:
  447.     start_timer(&ap->timer);
  448.     ap->proc_run = Waiting;
  449. }
  450.  
  451.